#include	<stdarg.h>		/* ANSI C header file */
#include	<syslog.h>		/* for syslog() */
#include 	"public.h"

#define HAVE_VSNPRINTF 1
#define MAXLINE 1024

int	daemon_proc = 0;		/* set nonzero by daemon_init() */


static void	err_doit(int, int, const char *, va_list);

/* Nonfatal error related to a system call.
 * Print a message and return. */

/* $$.ix [err_ret]~function,~source~code$$ */
void
err_ret(const char *fmt, ...)
{
	va_list		ap;

	va_start(ap, fmt);
	err_doit(1, LOG_INFO, fmt, ap);
	va_end(ap);
	return;
}

/* Fatal error related to a system call.
 * Print a message and terminate. */

/* $$.ix [err_sys]~function,~source~code$$ */
void
err_sys(const char *fmt, ...)
{
	va_list		ap;

	va_start(ap, fmt);
	err_doit(1, LOG_ERR, fmt, ap);
	va_end(ap);
	exit(1);
}

/* Fatal error related to a system call.
 * Print a message, dump core, and terminate. */

/* $$.ix [err_dump]~function,~source~code$$ */
void
err_dump(const char *fmt, ...)
{
	va_list		ap;

	va_start(ap, fmt);
	err_doit(1, LOG_ERR, fmt, ap);
	va_end(ap);
	abort();		/* dump core and terminate */
	exit(1);		/* shouldn't get here */
}

/* Nonfatal error unrelated to a system call.
 * Print a message and return. */

/* $$.ix [err_msg]~function,~source~code$$ */
void
err_msg(const char *fmt, ...)
{
	va_list		ap;

	va_start(ap, fmt);
	err_doit(0, LOG_INFO, fmt, ap);
	va_end(ap);
	return;
}

/* Fatal error unrelated to a system call.
 * Print a message and terminate. */

/* $$.ix [err_quit]~function,~source~code$$ */
void
err_quit(const char *fmt, ...)
{
	va_list		ap;

	va_start(ap, fmt);
	err_doit(0, LOG_ERR, fmt, ap);
	va_end(ap);
	exit(1);
}

/* Print a message and return to caller.
 * Caller specifies "errnoflag" and "level". */

/* $$.ix [err_doit]~function,~source~code$$ */
static void
err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
	int		errno_save, n;
	char	buf[MAXLINE];

	errno_save = errno;		/* value caller might want printed */
#ifdef	HAVE_VSNPRINTF
	vsnprintf(buf, sizeof(buf), fmt, ap);	/* this is safe */
#else
	vsprintf(buf, fmt, ap);					/* this is not safe */
#endif
	n = strlen(buf);
	if (errnoflag)
		snprintf(buf+n, sizeof(buf)-n, ": %s", strerror(errno_save));
	strcat(buf, "\n");

	if (daemon_proc) {
		syslog(level, buf);
	} else {
		fflush(stdout);		/* in case stdout and stderr are the same */
		fputs(buf, stderr);
		fflush(stderr);
	}
	return;
}

void *Malloc(size_t size)
{
	void	*ptr;

	if ( (ptr = malloc(size)) == NULL)
		err_sys("malloc error");
	return(ptr);
}

void
Pthread_attr_init(pthread_attr_t *attr)
{
	int		n;

	if ( (n = pthread_attr_init(attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_attr_init error");
}

void
Pthread_attr_destroy(pthread_attr_t *attr)
{
	int		n;

	if ( (n = pthread_attr_destroy(attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_attr_destroy error");
}

void
Pthread_attr_setdetachstate(pthread_attr_t *attr, int detach)
{
	int		n;

	if ( (n = pthread_attr_setdetachstate(attr, detach)) == 0)
		return;
	errno = n;
	err_sys("pthread_attr_setdetachstate error");
}

void
Pthread_attr_setscope(pthread_attr_t *attr, int scope)
{
	int		n;

	if ( (n = pthread_attr_setscope(attr, scope)) == 0)
		return;
	errno = n;
	err_sys("pthread_attr_setscope error");
}

void
Pthread_create(pthread_t *tid, const pthread_attr_t *attr,
			   void * (*func)(void *), void *arg)
{
	int		n;

	if ( (n = pthread_create(tid, attr, func, arg)) == 0)
		return;
	errno = n;
	err_sys("pthread_create error");
}

void
Pthread_join(pthread_t tid, void **status)
{
	int		n;

	if ( (n = pthread_join(tid, status)) == 0)
		return;
	errno = n;
	err_sys("pthread_join error");
}

void
Pthread_detach(pthread_t tid)
{
	int		n;

	if ( (n = pthread_detach(tid)) == 0)
		return;
	errno = n;
	err_sys("pthread_detach error");
}

void
Pthread_kill(pthread_t tid, int signo)
{
	int		n;

	if ( (n = pthread_kill(tid, signo)) == 0)
		return;
	errno = n;
	err_sys("pthread_kill error");
}

void
Pthread_mutexattr_init(pthread_mutexattr_t *attr)
{
	int		n;

	if ( (n = pthread_mutexattr_init(attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_mutexattr_init error");
}

void
Pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
{
	int		n;

	if ( (n = pthread_mutexattr_destroy(attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_mutexattr_destroy error");
}

#ifdef	_POSIX_THREAD_PROCESS_SHARED
void
Pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int flag)
{
	int		n;

	if ( (n = pthread_mutexattr_setpshared(attr, flag)) == 0)
		return;
	errno = n;
	err_sys("pthread_mutexattr_setpshared error");
}
#endif

void Pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
{
	int		n;

	if ( (n = pthread_rwlock_init(rwlock, attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_rwlock_init error");
}

void Pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
{
	int		n;

	if ( (n = pthread_rwlock_destroy(rwlock)) == 0)
		return;
	errno = n;
	err_sys("pthread_rwlock_destroy error");
}

void Pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
{
	int		n;

	if ( (n = pthread_rwlock_rdlock(rwlock)) == 0)
		return;
	errno = n;
	err_sys("Pthread_rwlock_rdlock error");
}

void Pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
{
	int		n;

	if ( (n = pthread_rwlock_wrlock(rwlock)) == 0)
		return;
	errno = n;
	err_sys("pthread_rwlock_wrlock error");
}

void Pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
{
	int		n;

	if ( (n = pthread_rwlock_unlock(rwlock)) == 0)
		return;
	errno = n;
	err_sys("pthread_rwlock_unlock error");
}


void
Pthread_mutex_init(pthread_mutex_t *mptr, pthread_mutexattr_t *attr)
{
	int		n;

	if ( (n = pthread_mutex_init(mptr, attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_mutex_init error");
}

void
Pthread_cond_init(pthread_cond_t *mptr, pthread_condattr_t *attr)
{
	int		n;

	if ( (n = pthread_cond_init(mptr, attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_cond_init error");
}

void
Pthread_mutex_destroy(pthread_mutex_t *mptr)
{
	int		n;

	if ( (n = pthread_mutex_destroy(mptr)) == 0)
		return;
	errno = n;
	err_sys("pthread_mutex_destroy error");
}

/* include Pthread_mutex_lock */
void
Pthread_mutex_lock(pthread_mutex_t *mptr)
{
	int		n;

	if ( (n = pthread_mutex_lock(mptr)) == 0)
		return;
	errno = n;
	err_sys("pthread_mutex_lock error");
}
/* end Pthread_mutex_lock */

void
Pthread_mutex_unlock(pthread_mutex_t *mptr)
{
	int		n;

	if ( (n = pthread_mutex_unlock(mptr)) == 0)
		return;
	errno = n;
	err_sys("pthread_mutex_unlock error");
}

void
Pthread_condattr_init(pthread_condattr_t *attr)
{
	int		n;

	if ( (n = pthread_condattr_init(attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_condattr_init error");
}

void
Pthread_condattr_destroy(pthread_condattr_t *attr)
{
	int		n;

	if ( (n = pthread_condattr_destroy(attr)) == 0)
		return;
	errno = n;
	err_sys("pthread_condattr_destroy error");
}

#ifdef	_POSIX_THREAD_PROCESS_SHARED
void
Pthread_condattr_setpshared(pthread_condattr_t *attr, int flag)
{
	int		n;

	if ( (n = pthread_condattr_setpshared(attr, flag)) == 0)
		return;
	errno = n;
	err_sys("pthread_condattr_setpshared error");
}
#endif

void
Pthread_cond_broadcast(pthread_cond_t *cptr)
{
	int		n;

	if ( (n = pthread_cond_broadcast(cptr)) == 0)
		return;
	errno = n;
	err_sys("pthread_cond_broadcast error");
}

void
Pthread_cond_signal(pthread_cond_t *cptr)
{
	int		n;

	if ( (n = pthread_cond_signal(cptr)) == 0)
		return;
	errno = n;
	err_sys("pthread_cond_signal error");
}

void
Pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr)
{
	int		n;

	if ( (n = pthread_cond_wait(cptr, mptr)) == 0)
		return;
	errno = n;
	err_sys("pthread_cond_wait error");
}

int 
Pthread_cond_timedwait(pthread_cond_t *cptr, pthread_mutex_t *mptr,
					   const struct timespec *tsptr)
{
	int		n;

	n = pthread_cond_timedwait(cptr, mptr, tsptr);
	errno = n;
	if (n != ETIMEDOUT)
		err_sys("pthread_cond_timedwait error");
	return n;
}

void
Pthread_once(pthread_once_t *ptr, void (*func)(void))
{
	int		n;

	if ( (n = pthread_once(ptr, func)) == 0)
		return;
	errno = n;
	err_sys("pthread_once error");
}

void
Pthread_key_create(pthread_key_t *key, void (*func)(void *))
{
	int		n;

	if ( (n = pthread_key_create(key, func)) == 0)
		return;
	errno = n;
	err_sys("pthread_key_create error");
}

void
Pthread_setcancelstate(int state, int *oldstate)
{
	int		n;

	if ( (n = pthread_setcancelstate(state, oldstate)) == 0)
		return;
	errno = n;
	err_sys("pthread_setcancelstate error");
}

void
Pthread_setspecific(pthread_key_t key, const void *value)
{
	int		n;

	if ( (n = pthread_setspecific(key, value)) == 0)
		return;
	errno = n;
	err_sys("pthread_setspecific error");
}

/* include pr_thread_id */
long
pr_thread_id(pthread_t *ptr)
{
#if defined(sun)
	return((ptr == NULL) ? pthread_self() : *ptr);	/* Solaris */

#elif defined(__osf__) && defined(__alpha)
	pthread_t	tid;

	tid = (ptr == NULL) ? pthread_self() : *ptr;	/* Digital Unix */
	return(pthread_getsequence_np(tid));
#else
		/* 4everything else */
	return((ptr == NULL) ? pthread_self() : *ptr);
#endif
}

void
Sem_init(sem_t *sem, int pshared, unsigned int value)
{
	if (sem_init(sem, pshared, value) == -1)
		err_sys("sem_init error");
}

void
Sem_destroy(sem_t *sem)
{
	if (sem_destroy(sem) == -1)
		err_sys("sem_destroy error");
}

void
Sem_wait(sem_t *sem)
{
	if (sem_wait(sem) == -1)
		err_sys("sem_wait error");
}

int
Sem_trywait(sem_t *sem)
{
	int		rc;

	if ( (rc = sem_trywait(sem)) == -1 && errno != EAGAIN)
		err_sys("sem_trywait error");
	return(rc);
}

/* include Sem_post */
void
Sem_post(sem_t *sem)
{
	if (sem_post(sem) == -1)
		err_sys("sem_post error");
}
/* end Sem_post */

int
Ioctl(int fd, int request, void *arg)
{
	int		n;

	if ( (n = ioctl(fd, request, arg)) == -1)
		err_sys("ioctl error");
	return(n);	/* streamio of I_LIST returns value */
}

int
Socket(int family, int type, int protocol)
{
	int		n;

	if ( (n = socket(family, type, protocol)) < 0)
		err_sys("socket error");
	return(n);
}



int SOCKET_GetLocalIp(char* inter_face, char* ip_addr)
{

  	   int sock_ip;
       struct ifreq ifr_ip;
       struct sockaddr_in sin;

	   memset(&sin, 0, sizeof(struct sockaddr_in));

       sock_ip = Socket( AF_INET, SOCK_STREAM, 0 );
       memset(&ifr_ip, 0, sizeof(ifr_ip));
       strncpy(ifr_ip.ifr_name, inter_face, sizeof(ifr_ip.ifr_name)-1);
       Ioctl(sock_ip, SIOCGIFADDR, &ifr_ip);

       memcpy(&sin, &ifr_ip.ifr_addr, sizeof(sin));
       close(sock_ip);
	   memset(ip_addr, 0, sizeof(ip_addr));
       // memcpy(ip_addr,inet_ntoa(sin.sin_addr),strlen(inet_ntoa(sin.sin_addr)));
	   strcpy(ip_addr, inet_ntoa(sin.sin_addr));
       return 0;
}


void *
Calloc(size_t n, size_t size)
{
	void	*ptr;

	if ( (ptr = calloc(n, size)) == NULL)
		err_sys("calloc error");
	return(ptr);
}


struct hash_list *hash_create(unsigned int mode, 
							unsigned int size, 
							hash_count_cp count_cp,
							hash_compare_cp compare_cp,
							hash_copy_key_cp copy_key_cp,
							hash_copy_value_cp copy_value_cp)
{
	if (size == 0)
		return NULL;
	struct hash_list *pst;
	pst = malloc(sizeof(struct hash_list));
	if (pst == NULL)
		return NULL;
	pst->mode = mode;
	pst->table_size = size;
	pst->ppstHash_table = (struct hash_list_entry **)calloc(size, sizeof(struct hash_list_entry *));
	if (pst->ppstHash_table == NULL)
		goto err2;
	/*
	pst->pstOp = (struct hash_list_operation *)malloc(sizeof(struct hash_list_operation));
	if (pst->pstOp == NULL)
		goto err1;
	*/
	
	//memcpy(pst->pstOp, pstAttr, sizeof(struct hash_list_operation));
	pst->fun_count = count_cp;
	pst->fun_compare = compare_cp;
	pst->fun_copy_key = copy_key_cp;
	pst->fun_copy_value = copy_value_cp;
	Pthread_rwlock_init(&pst->lock, NULL);
	return pst;
	free(pst->ppstHash_table);
err2:
	free(pst);
	return NULL;
}

static struct hash_list_entry *hash_create_entry(struct hash_list *self, void *key, void *value)
{
	struct hash_list_entry *pst;
	hash_copy_key_cp ck;
	hash_copy_value_cp cv;
	if (self == NULL || key == NULL || value == NULL)
	{
		if (self == NULL)
			err_msg("self point to the NULL");
		else if (key == NULL)
			err_msg("key point to the NULL");
		else if (value == NULL)
			err_msg("value point to the NULL");
		return NULL;
	}
	ck = self->fun_copy_key;
	cv = self->fun_copy_value;

	pst = Calloc(1, sizeof(struct hash_list_entry));
	pst->key = ck ? (*ck)(key) : key;
	pst->value = cv ? (*cv)(value) : value;
	return pst;
}

void *hash_search_list(struct hash_list *self, void *key)
{
	unsigned long index;
	void *pValue;
	struct hash_list_entry *pstEntry;
	Pthread_rwlock_rdlock(&self->lock);
	index = abs((*self->fun_count)(key)) % self->table_size;
	pstEntry = self->ppstHash_table[index];
	while(pstEntry && (*self->fun_compare)(pstEntry->key, key))
		pstEntry = pstEntry->pstList;
	if (pstEntry == NULL)
	{
		Pthread_rwlock_unlock(&self->lock);
		return NULL;
	}
	pValue = pstEntry->value;	//be protected by rw_lock
	Pthread_rwlock_unlock(&self->lock);
	return pValue;
}

struct hash_list_entry *hash_insert_list(struct hash_list *self, void *key, void *value)
{
	unsigned long index;
	struct hash_list_entry **ppstInsert;
	struct hash_list_entry *pstEntry;
	if ((pstEntry = hash_create_entry(self, key, value)) == NULL)
		return NULL;
	index = (*self->fun_count)(key);
	index = abs(index) % self->table_size;
	//index = abs((*self->pstOp->fun_count)(key)) % self->table_size;
	Pthread_rwlock_wrlock(&self->lock);
	ppstInsert = &self->ppstHash_table[index];
	while(*ppstInsert != NULL)
		ppstInsert = &(*ppstInsert)->pstList;
	
	*ppstInsert = pstEntry;
	
	self->element_size++;
	Pthread_rwlock_unlock(&self->lock);
	return pstEntry;
}

int hash_del_entry(struct hash_list *self, void *key)
{
	struct hash_list_entry **ppstDel;
	struct hash_list_entry *pstDel;
	unsigned long index;
	index = abs((*self->fun_count)(key)) % self->table_size;
	Pthread_rwlock_wrlock(&self->lock);
	ppstDel = &self->ppstHash_table[index];
	while(*ppstDel && (*self->fun_compare)((*ppstDel)->key, key))
	{
		ppstDel = &(*ppstDel)->pstList;
	}
	if (*ppstDel == NULL)
	{
		Pthread_rwlock_unlock(&self->lock);
		return -1;
	}
	pstDel = *ppstDel;
	*ppstDel = (*ppstDel)->pstList;
	free(pstDel);
	self->element_size--;
	Pthread_rwlock_unlock(&self->lock);
	return 0;
}

int hash_destroy(struct hash_list *self)
{
	int i;
	struct hash_list_entry *pstEntry, *pos, *pnext;
	//struct hash_list_entry **ppstStack;
	//struct hash_list_entry **ppstStackTop;
	Pthread_rwlock_wrlock(&self->lock);
	for (i=0; i<self->table_size; i++)
	{
		pstEntry = self->ppstHash_table[i];
		if (pstEntry == NULL)
			continue;
		for (pos=pstEntry; pos!=NULL ;pos=pnext)
		{
			pnext = pos->pstList;
			free(pos);	
		}
	}
	/*
	ppstStackTop = ppstStack = Malloc(self->element_size * sizeof(struct hash_list_entry *));
	for (i=0; i<self->table_size; i++)
	{
		pstEntry = self->ppstHash_table[i];
		while(pstEntry)
		{
			*ppstStack = pstEntry;
			ppstStack++;
			pstEntry = pstEntry->pstList;
		}
	}
	
	do
	{
		ppstStack--;
		free(*ppstStack);		
	}
	while(ppstStack != ppstStackTop);

	free(ppstStackTop);
	*/
	Pthread_rwlock_unlock(&self->lock);
	Pthread_rwlock_destroy(&self->lock);
	free(self->ppstHash_table);
	//free(self->pstOp);
	free(self);
	return 0;
}

FILE *Fopen(const char *pathname, const char *mode)
{
	FILE	*fp;

	if ( (fp = fopen(pathname, mode)) == NULL)
	{
		perror("fopen error");
		exit(1);
	}
	return(fp);
}


int Fcntl(int fd, int cmd, int arg)
{
	int	n;

	if ( (n = fcntl(fd, cmd, arg)) == -1)
		err_sys("fcntl error");
	return(n);
}

int Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
       struct timeval *timeout)
{
	int		n;

	if ( (n = select(nfds, readfds, writefds, exceptfds, timeout)) < 0)
		err_sys("select error");
	return(n);		/* can return 0 on timeout */
}



void Send(int fd, const void *ptr, size_t nbytes, int flags)
{
	if (send(fd, ptr, nbytes, flags) != (ssize_t)nbytes)
		err_sys("send error");
}

int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec)
{
	int				flags, n, error;
	socklen_t		len;
	fd_set			rset, wset;
	struct timeval	tval;

	flags = Fcntl(sockfd, F_GETFL, 0);
	Fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

	error = 0;
	if ( (n = connect(sockfd, saptr, salen)) < 0)
		if (errno != EINPROGRESS)
			return(-1);

	/* Do whatever we want while the connect is taking place. */

	if (n == 0)
		goto done;	/* connect completed immediately */

	FD_ZERO(&rset);
	FD_SET(sockfd, &rset);
	wset = rset;
	tval.tv_sec = nsec;
	tval.tv_usec = 0;

	if ( (n = Select(sockfd+1, &rset, &wset, NULL,
					 nsec ? &tval : NULL)) == 0) {
		close(sockfd);		/* timeout */
		errno = ETIMEDOUT;
		return(-1);
	}

	if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
		len = sizeof(error);
		if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
			return(-1);			/* Solaris pending error */
	} else
		err_quit("select error: sockfd not set");

done:
	Fcntl(sockfd, F_SETFL, flags);	/* restore file status flags */

	if (error) {
		close(sockfd);		/* just in case */
		errno = error;
		return(-1);
	}
	return(0);
}

